<!doctype html>
<!-- 声明文档类型为 HTML5，告知浏览器按照 HTML5 标准解析该文档 -->
<html lang="en">
<!-- 定义 HTML 文档的根元素，lang 属性指定文档语言为英语 -->
<head>
    <!-- 设置文档的字符编码为 UTF-8，确保页面能正确显示各种语言字符 -->
    <meta charset="UTF-8">
    <!-- 配置视口，使页面在不同设备上自适应显示，width=device-width 表示页面宽度等于设备宽度，initial-scale=1 表示初始缩放比例为 1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 设置页面在浏览器标签页上显示的标题 -->
    <title>方案5-判断初始化复杂json多层数据-使用lodash库</title>
    <!-- 引入 Lodash 库，该库提供了许多实用的工具函数，这里用于简化对象处理逻辑 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.min.js"></script>
</head>
<body>
<!-- 创建一个 div 容器，用于包裹段落元素 -->
<div>
    <!-- 创建一个段落元素，id 为 test，用于后续显示处理后的 JSON 数据 -->
    <p id="test">原始内容将被替换</p>
</div>
<script>
    /**
     * processJsonData 函数的主要目的是处理 JSON 数据，将其中的 null 值根据 typeConfig 中指定的默认值进行替换。
     * @param {string|object} jsonData - 需要处理的 JSON 数据，可以是 JSON 字符串或者 JavaScript 对象。
     * @param {object} typeConfig - 配置对象，用于指定 jsonData 中各个字段的默认值。
     * @returns {object|null} - 处理后的 JavaScript 对象，如果解析失败则返回 null。
     */
    function processJsonData(jsonData, typeConfig) {
        // 检查 typeConfig 是否为有效的对象，若不是对象或者为 null，则输出错误信息并返回 null
        if (typeof typeConfig !== 'object' || typeConfig === null) {
            console.error('typeConfig 必须是一个对象');
            return null;
        }
        try {
            // 判断 jsonData 的类型，如果是字符串，则尝试使用 JSON.parse 方法将其解析为 JavaScript 对象；如果本身就是对象，则直接使用
            const data = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData;
            // 使用 Lodash 的 cloneDeepWith 函数对 data 进行深拷贝，并在拷贝过程中处理 null 值
            return _.cloneDeepWith(data, (value, key) => {
                // 若当前值为 null 且 typeConfig 中该键对应的默认值存在，则返回默认值
                if (value === null && typeConfig[key] !== undefined) return typeConfig[key];
                // 如果当前值是对象且 typeConfig 中对应的值也是对象，则递归处理嵌套对象
                if (_.isObject(value) && _.isObject(typeConfig[key])) {
                    return _.cloneDeepWith(value, (subValue, subKey) =>
                        // 若子值为 null 且 typeConfig 中对应子键的默认值存在，则返回默认值
                        subValue === null && typeConfig[key][subKey] !== undefined ? typeConfig[key][subKey] : undefined
                    );
                }
            });
        } catch (error) {
            // 若 JSON 解析过程中出现错误，捕获该错误并输出错误信息，同时返回 null
            console.error('JSON 解析失败:', error);
            return null;
        }
    }

    // 定义一个示例 JSON 数据，包含多个值为 null 的字段
    const jsonData = {
        "userList": [{
            "userListSon": null,
            "deptListSon": []
        }],
        "deptList": null,
        "userInfo": {"username": "abc", "likeArray": null},
        "userName": null,
        "userAge": null
    };
    // 定义一个配置对象，为 jsonData 中的每个字段指定默认值
    const typeConfig = {
        "userList": [{
            "userListSon": [],
            "deptListSon": []
        }],
        "deptList": [], // deptList 字段的默认值为一个空数组
        "userInfo": {
            "username": "", // userInfo 中 username 字段的默认值为一个空字符串
            "likeArray": [] // userInfo 中 likeArray 字段的默认值为一个空数组
        },
        "userName": "", // userName 字段的默认值为一个空字符串
        "userAge": 0    // userAge 字段的默认值为数字 0
    };

    // 调用 processJsonData 函数处理 jsonData，传入 jsonData 和 typeConfig 作为参数
    const processedData = processJsonData(jsonData, typeConfig);
    // 检查处理后的数据是否存在（不为 null）
    if (processedData) {
        // 若处理后的数据存在，则将其打印到控制台，方便调试查看
        console.log(processedData);
        // 通过 document.getElementById 方法获取 id 为 test 的 HTML 元素
        // 然后将处理后的 JSON 数据 processedData 转换为 JSON 字符串
        // 并将该字符串赋值给获取到的元素的 innerHTML 属性，从而更新页面显示
        document.getElementById("test").innerHTML = JSON.stringify(processedData);
    }
</script>
</body>
</html>
